/*
 * Copyright 2009, Qys Creations 
 * 
 *  This file is part of Chameleon DB.
 *
 *  Chameleon DB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Chameleon DB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Chameleon DB.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * */

import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class db_page {
	public byte[] bData = new byte[db_util.DB_PAGE_SIZE];
	public byte[] bDataWrite;
	private db_lock oLock = new db_lock();

	private db_lock oWriteLock = new db_lock();
	private boolean bIsDirty = true;

	public long lGroup = 0;
	private long lFilePosition = 0;

	db_page(long lGroupVal, long lFilePositionVal) {
		lFilePosition = lFilePositionVal;
		lGroup = lGroupVal;
	}

	public byte[] read(FileChannel oFile) {
		byte[] bReturnData = new byte[db_util.DB_PAGE_SIZE];
		try {
			if (bIsDirty) {
				//System.out.println("Pos: " + lFilePosition + " thread: " + Thread.currentThread().getId() + " Page miss");
				synchronized(oFile) {
					oFile.position(lFilePosition);
					ByteBuffer oBuf = ByteBuffer.allocate(db_util.DB_PAGE_SIZE);
					oBuf.clear();
					oFile.read(oBuf);
					bData = oBuf.array();
					bIsDirty = false;
				}
			}
			
			//db_bytes oBytes = new db_bytes(bData);
			//System.out.println(Thread.currentThread().getId() + " reading " + oBytes.readLong() + " " + oBytes.readLong());
			//System.arraycopy(bData, 0, bReturnData, 0, db_util.DB_PAGE_SIZE);
			
			oLock.getReadLock();
				bReturnData = bData.clone();
			oLock.releaseReadLock();
		} catch (Exception e) {e.printStackTrace();}
		return bReturnData;
	}

	// The order of a write is the following
	
	
	// begin();
	// write(bytes[]);
	// write(bytes[]);
	// ...
	// ...
	// commit(); <- all written pages must commit before they save
	// save(); <- all written pages must save before they finish
	// finish(); <- must finish in order
	
	// The idea is that a write starts by claiming the page write lock
	
	// Once the lock is obtained, the thread can write however much it wants to the page
	
	// When the thread is done writing to the page(s) it will either commit or rollback
	
	// A rollback will simply abandon the writes
	
	// A commit will claim another lock that indicates that reads cannot take place
	
	// The idea is to avoid problems like the following:
	// Page1, Page2 and Page3 has been written to.
	
	// Now just flushing Page1 contents to disk could cause a reading thread to get half updated
	// contents if the write crossed Page1 and Page2
	
	// Therefore a write lock is necessary to ensure that reads do not occur while the pages(s) are being updated.
	
	public void rollback() {
		try {
			oWriteLock.releaseWriteLock();
			bDataWrite = null;
		} catch (Exception e) {e.printStackTrace();}
	}
	
	public db_page begin() {
		try {
			oWriteLock.getWriteLock();
			bDataWrite = bData.clone();
		} catch (Exception e) {e.printStackTrace();}
		return this;
	}

	public void save(FileChannel oFile) {
		try {
			synchronized(oFile) {
				//System.out.println("writing to file.. going for a nap");
				
				oFile.position(lFilePosition);
				ByteBuffer oBuf = ByteBuffer.allocate(db_util.DB_PAGE_SIZE);
				oBuf.put(bDataWrite);
				oBuf.clear();
				oFile.write(oBuf);
				oFile.force(false);
				bIsDirty = false;
				bData = bDataWrite.clone();
			}
		} catch (Exception e) {e.printStackTrace();}
	}

	public void commit() {
		try {
			oLock.getWriteLock();
		} catch (Exception e) {e.printStackTrace();}
	}

	public void finish() {
		try {
			oLock.releaseWriteLock();
			oWriteLock.releaseWriteLock();
		} catch (Exception e) {e.printStackTrace();}
	}

	public void write(byte[] bNewData){
		bDataWrite = bNewData.clone();
	}
}
